Doubly Linked List
class LinkedListNode { // LinkedListNode class
constructor(value, next = null, prev = null) {
this.value = value; //actual value to store (num/string/bool/obj)
this.next = next; //pointer to next node
this.prev = prev; //pointer to prev node
}
toString(callback) {
return callback ? callback(this.value) : `${this.value}`
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
prepend(value) {
if(!this.head) {
this.head = newNode;
this.tail = newNode;
this.length++;
return this;
}
const newNode = new LinkedListNode(value, this.head);
this.head.prev = newNode
this.head = newNode;
this.length++;
return this;
}
append(value) {
const newNode = new LinkedListNode(value);
if(!this.head) {
this.head = newNode;
this.tail = newNode;
this.length++;
return this;
}
const currentTail = this.tail;
currentTail.next = newNode;
newNode.prev = this.tail; //tail is now the 2nd last
this.tail = newNode;
this.length++;
return this;
}
delete(value) {
if(!this.head) { //is linked list empty?
return null;
}
let deletedNode = null;
let nextNode;
// Is the node to be deleted the head?
if (this.head && this.head.value === value) {
deletedNode = this.head; //set deletedNode to it.
nextNode = this.head.next;
this.head = nextNode;
nextNode.prev = null;
deletedHead.next = null;
deletedHead.prev = null;
this.length--;
return deletedNode; //return the deleted node
}
let currentNode = this.head;
if (currentNode !== null) { //check it isn't null
while (currentNode.next) { //as long as the next node exists
if (currentNode.next.value === value) { //check value
deletedNode = currentNode.next; //update deletedNode
nextNode = deletedNode.next;
currentNode.next = currentNode.next.next;
nextNode.prev = deletedNode.prev;
deletedNode.next = null;
deletedNode.prev = null;
this.length--;
)
} else {
currentNode = currentNode.next; x
}
}
}
// Is the node to be deleted the tail?
if (this.tail.value === value) {
this.tail = currentNode;
this.length--;
}
return deletedNode; //return the deleted node
}
deleteTail() { //(pop) removes last node (list's tail)
//No nodes?
if (!this.head) {
return null; //or undefined
}
const deletedTail = this.tail; //tail to be deleted/returned
if (this.head === this.tail) {
this.head = null;
this.tail = null;
this.length = 0;
return deletedTail;
}
this.tail = deletedTail.prev;
deletedTail.prev = null;
this.tail.next = null;
this.length--;
return deletedTail;
}
deleteHead() { //(shift) removes first node
//No nodes?
if (!this.head) {
return null;// or undefined
}
//1 or more nodes?
const deletedHead = this.head; //capture the value to
//be returned
if (this.head.next) { //next exists? More than 1
this.head = deletedHead.next;
deletedHead.next = null;
this.head.prev = null;
this.length--;
} else {
this.head = null;
this.tail = null;
this.length = 0;
}
return deletedHead;
}
//locates first item that matches value
//takes value to match and callback
find({ value = undefined, callback = undefined }) {
//No node? List empty!
if (!this.head) {
return null; //or undefined
}
//track current position in list.
let currentNode = this.head;
while (currentNode) {
// If callback is specified then try to find node by callback.
if (callback && callback(currentNode.value)) {
return currentNode; //return found node
}
// If value is specified then try to compare by value..
if (value !== undefined && currentNode.value === value) {
return currentNode; //return found node
}
currentNode = currentNode.next; //similar to i++ (move on to next)
}
return null; //didn't find? null!
}
//Retrieve a node by its position
get(index) {
//not possible => null
if(index < 0 || index >= this.length) {
return null;
}
let count, currentNode;
//index closer to the beginning => start from head, move forward
if(index <= this.length/2) {
count = 0;
currentNode = this.head;
while(count != index) {
currentNode = currentNode.next; //i++
count++;
}
} else { //index closer to the end => start from tail, move back
counter = this.length - 1;
currentNode = this.tail;
while(count != index) {
currentNode = currentNode.prev;
count--;
}
}
return currentNode;
}
//Change a node's value by its position
set(index, value) {
let foundNode = this.get(index);
if(foundNode) {
foundNode.value = value;
return true;
}
return false;
}
//insert new node at a specific position
insert(index, value) {
if(index < 0 || index > this.length) {
return false;
}
if(index === this.length) { //insert at end (tail)
return !!this.append(value);
}
if(index === 0) {//insert at start (head)
return !!this.prepend(value);
}
//insertion at any other position
let prevNode = get(index - 1);
let nextNode = prevNode.next;
let newNode = new LinkedListNode(value, nextNode)
nextNode.prev = newNode;
newNode.prev = prevNode
prevNode.next = newNode;
this.length++;
return true;
}
//reversing the list in place
reverse() {
//track our current position in the list
//13 -> 27 -> 32 -> 71
let currentNode = this.head; //13
this.head = this.tail;
this.tail = currentNode;
let prevNode = null;
let nextNode;
while(currentNode) {
nextNode = currentNode.next; //27
currentNode.next = prevNode; //13 -> null
prevNode = currentNode; //13
currentNode = nextNode; //27
}
currentNode.next = currentNode.next.next;
}
toArray() {
const nodes = [];
let currentNode = this.head;
while (currentNode) {
nodes.push(currentNode);
currentNode = currentNode.next;
}
return nodes;
}
toString(callback) {
return this.toArray().map(node => node.toString(callback)).toString()
}
}
Discussion (5 comments)